package com.joe.core;

import com.joe.entity.exceptions.SqlException;
import com.joe.entity.tuples.TwoTuple;
import com.joe.utils.ObjectUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by joe on 2018/12/2
 */
public class SqlMapper {
    private static final Pattern INDEX_PATTERN = Pattern.compile("\\{(?<INDEX>\\d+)}");
    private static final Pattern FIELD_PATTERN = Pattern.compile("\\$\\{(?<FIELD>.*?)}");

    private static SqlMapper sqlMapper;

    public static SqlMapper getInstance() {
        if (sqlMapper == null) {
            sqlMapper = new SqlMapper();
        }
        return sqlMapper;
    }

    public TwoTuple<String, Object[]> doMapping(String preSql, Object[] args) throws SqlException, NoSuchFieldException, IllegalAccessException {
        Object[] resultArgs;
        if (ObjectUtils.isEmpty(resultArgs = indexHandle(preSql, args))) {
            resultArgs = beanHandle(preSql, args[0]);  // TODO 此处添加多参数处理
        }
        return new TwoTuple<>(preSql.replaceAll("['\"]?\\$?\\{.*?}['\"]?", "?"), resultArgs);
    }

    /**
     * 占位符式的sql处理
     */
    private Object[] indexHandle(String preSql, Object[] args) throws SqlException {
        List<Object> resultList = new ArrayList<>();

        Matcher matcher = INDEX_PATTERN.matcher(preSql);
        while (matcher.find()) {
            String indexStr = matcher.group("INDEX");
            int argIndex = Integer.parseInt(indexStr);
            if (argIndex >= args.length) {
                throw new SqlException("Sql mapping error! Can not find the arg of index -> " + argIndex);
            }
            resultList.add(args[argIndex]);
        }
        return resultList.toArray();
    }

    /**
     * bean字段式的sql处理
     */
    // TODO 先简单的完成功能，
    // TODO 自己实现BeanUtils，
    // TODO 研究apache beanUtils，改善自己开发的BeanUtils
    private Object[] beanHandle(String preSql, Object bean) throws NoSuchFieldException, IllegalAccessException {
        List<Object> resultList = new ArrayList<>();

        Class clazz = bean.getClass();

        Matcher matcher = FIELD_PATTERN.matcher(preSql);
        while (matcher.find()) {
            String fieldName = matcher.group("FIELD");
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            Object result = field.get(bean);
            resultList.add(result);
        }
        return resultList.toArray();
    }
}
